package com.netvine.iias;

import com.sun.jna.Native;
import com.sun.jna.NativeLong;
import com.sun.jna.Pointer;
import com.sun.jna.examples.win32.W32API;
import com.sun.jna.ptr.IntByReference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

import javax.swing.*;
import java.io.*;
import java.util.*;

@Component
public class run {
    private static final Logger log = LoggerFactory.getLogger(run.class);
    @Value("${nvr.ip}")
    private String ip;

    @Value("${nvr.port}")
    private Integer port;

    @Value("${nvr.account}")
    private String account;

    @Value("${nvr.password}")
    private String password;
    public static HCNetSDK hCNetSDK = null;
    private static HCNetSDK.NET_DVR_WORKSTATE_V30 devwork;

    static HCNetSDK.NET_DVR_USER_LOGIN_INFO m_strLoginInfo = null;
    static HCNetSDK.NET_DVR_DEVICEINFO_V40 m_strDeviceInfo = null;
    private Integer previewHandle;
    private static List<Integer> posList = new ArrayList<>();
    private static Map<Integer, List<Integer>> posMap = new HashMap<>();

    static int lUserID;//用户句柄

    public List<CameraInfo> getDeviceList() {
        if (hCNetSDK == null) {
            createSDKInstance();
        }
        //初始化
        hCNetSDK.NET_DVR_Init();
        System.out.println(ip);
        Resource resource = new ClassPathResource("application.yml");
        InputStream inputStream = null;
        try {
            inputStream = resource.getInputStream();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        ArrayList<CameraInfo> strings = new ArrayList<>();
        boolean login = login(ip, Short.parseShort(port.toString()), account, password);
        if (login) {
            List<Camera> deviceList = getDeviceList(ip, Short.parseShort(port.toString()), account, password);
            FileOutputStream fileOutputStream = null;
            PrintWriter printWriter = null;
            try {
                fileOutputStream = new FileOutputStream("D:\\ideaProject\\NvrDeviceList\\out1.txt");
                printWriter = new PrintWriter(fileOutputStream);
                for (Camera camera : deviceList) {
                    printWriter.println("摄像头名称：" + camera.getName() + " 摄像头ip：" + camera.getIp() + " 摄像头端口：" + camera.getPort() + " 摄像头状态：" + camera.getStatus() + " 摄像头rtsp：" + camera.getRtsp() + " 摄像头账户：" + camera.getAccount());
                    CameraInfo cameraInfo = new CameraInfo();
                    cameraInfo.setName(camera.getName());
                    cameraInfo.setIp(camera.getIp());
                    cameraInfo.setPort(camera.getPort());
                    cameraInfo.setStatus(camera.getStatus().toString());
                    cameraInfo.setRtsp(camera.getRtsp());
                    cameraInfo.setAccount(camera.getAccount());
                    cameraInfo.setChanNum(camera.getChanNum().toString());
                    strings.add(cameraInfo);
                }
            } catch (FileNotFoundException e) {
                throw new RuntimeException(e);
            } finally {
                if (printWriter != null) {
                    printWriter.close();
                }
                if (fileOutputStream != null) {
                    try {
                        fileOutputStream.close();
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        } else {
            System.out.println("login error");
        }
        return strings;
    }

    public List<Camera> getDeviceList(String ip, Short port, String user, String psw) {
        //获取设备状态
        devwork = new HCNetSDK.NET_DVR_WORKSTATE_V30();
        if (!hCNetSDK.NET_DVR_GetDVRWorkState_V30(lUserID, devwork)) {
            System.out.println("get dev state faulier");
            throw new RuntimeException("获取设备状态失败");
        }
        System.out.println("dev state: " + devwork.dwDeviceStatic);

        int maxIpChannelNum;
        if (m_strDeviceInfo.struDeviceV30.byHighDChanNum == 0) {
            maxIpChannelNum = m_strDeviceInfo.struDeviceV30.byIPChanNum & 0xff;
        } else {
            maxIpChannelNum = ((int) ((m_strDeviceInfo.struDeviceV30.byHighDChanNum & 0xff) << 8));
        }
        int group = maxIpChannelNum / 64 <= 0 ? 0 : maxIpChannelNum / 64 - 1;
        ArrayList<Camera> list = new ArrayList<>();
        for (int i = 0; i <= group; i++) {
            IntByReference intByReference = new IntByReference(0);

            HCNetSDK.NET_DVR_IPPARACFG_V40 netDvrIpparacfgV40 = new HCNetSDK.NET_DVR_IPPARACFG_V40();
            boolean bRet = hCNetSDK.NET_DVR_GetDVRConfig(lUserID, HCNetSDK.NET_DVR_GET_IPPARACFG_V40, i, netDvrIpparacfgV40.getPointer(), netDvrIpparacfgV40.size(), intByReference);
            netDvrIpparacfgV40.read();

            if (bRet) {
                for (int chanNum = 0; chanNum < maxIpChannelNum; chanNum++) {
                    Camera camera = new Camera();
                    netDvrIpparacfgV40.struStreamMode[chanNum].read();
                    if (netDvrIpparacfgV40.struStreamMode[chanNum].byGetStreamType == 0) {
                        netDvrIpparacfgV40.struStreamMode[chanNum].uGetStream.setType(HCNetSDK.NET_DVR_IPCHANINFO.class);
                        netDvrIpparacfgV40.struStreamMode[chanNum].uGetStream.struChanInfo.read();
                        if (netDvrIpparacfgV40.struStreamMode[chanNum].uGetStream.struChanInfo.byEnable == 1) {
                            //表明该设备已启用
                            camera.setStatus(1);
                            if (isChanStateAvailabel(chanNum)) {
                                //通道状态正常
                                //设置预览推流地址
                                camera.setRtsp("rtsp://" + user + ":" + psw + "@" + ip + "/Streaming/Channels/" + (chanNum + 1) + "01");
                                camera.setChanNum(chanNum + chanNum + 64 * i + m_strDeviceInfo.struDeviceV30.byStartDChan);
                                camera.setIp(new String(netDvrIpparacfgV40.struIPDevInfo[chanNum].struIP.sIpV4).trim());
                                camera.setPort(Short.toString(netDvrIpparacfgV40.struIPDevInfo[chanNum].wDVRPort));
                                camera.setAccount(new String(netDvrIpparacfgV40.struIPDevInfo[chanNum].sUserName).trim());
                                list.add(camera);
                            }
                        }
                    }
                }
            }
        }
        return list;
    }

    private boolean isChanStateAvailabel(int chanNum) {
        return devwork.struChanStatic[chanNum].bySignalStatic == 0 && devwork.struChanStatic[chanNum].byHardwareStatic == 0;
    }

    public boolean createSDKInstance() {
        if (hCNetSDK == null) {
            synchronized (HCNetSDK.class) {
                String strDllPath = "";
                try {
                    if (System.getProperty("os.name").toLowerCase().contains("windows"))
                        //win系统加载库路径
                        strDllPath = System.getProperty("user.dir") + "\\lib\\HCNetSDK.dll";
                        //strDllPath = "D:\\ideaProject\\NvrDeviceList\\lib\\HCNetSDK.dll";
                    else if (System.getProperty("os.name").toLowerCase().contains("linux"))
                        //Linux系统加载库路径
                        strDllPath = "/home/lib/libhcnetsdk.so";
                    System.out.println(strDllPath);
//                        strDllPath = "/opt/alarm/lib/libhcnetsdk.so";
                    hCNetSDK = (HCNetSDK) Native.loadLibrary(strDllPath, HCNetSDK.class);
                } catch (Exception ex) {
                    System.out.println("loadLibrary: " + strDllPath + " Error: " + ex.getMessage());
                    return false;
                }
            }
        }
        return true;
    }

    public boolean loginDefault() {
        return login(ip, Short.parseShort(port.toString()), account, password);
    }

    //登录设备同时获取到设备状态
    private static boolean login(String ip, Short port, String user, String psw) {
        //注册
        m_strLoginInfo = new HCNetSDK.NET_DVR_USER_LOGIN_INFO();//设备登录信息
        m_strDeviceInfo = new HCNetSDK.NET_DVR_DEVICEINFO_V40();//设备信息

        String m_sDeviceIP = ip;//设备ip地址
        m_strLoginInfo.sDeviceAddress = new byte[HCNetSDK.NET_DVR_DEV_ADDRESS_MAX_LEN];
        System.arraycopy(m_sDeviceIP.getBytes(), 0, m_strLoginInfo.sDeviceAddress, 0, m_sDeviceIP.length());

        String m_sUsername = user;//设备用户名
        m_strLoginInfo.sUserName = new byte[HCNetSDK.NET_DVR_LOGIN_USERNAME_MAX_LEN];
        System.arraycopy(m_sUsername.getBytes(), 0, m_strLoginInfo.sUserName, 0, m_sUsername.length());

        String m_sPassword = psw;//设备密码
        m_strLoginInfo.sPassword = new byte[HCNetSDK.NET_DVR_LOGIN_PASSWD_MAX_LEN];
        System.arraycopy(m_sPassword.getBytes(), 0, m_strLoginInfo.sPassword, 0, m_sPassword.length());

        m_strLoginInfo.wPort = port;
        m_strLoginInfo.bUseAsynLogin = false; //是否异步登录：0- 否，1- 是
        m_strLoginInfo.byLoginMode = 0;  //0- SDK私有协议，1- ISAPI协议
        m_strLoginInfo.write();

        lUserID = hCNetSDK.NET_DVR_Login_V40(m_strLoginInfo, m_strDeviceInfo);
        System.out.println(lUserID);
        if (lUserID == -1) {
            log.info(ip);
            log.info(port.toString());
            log.info(psw);
            log.info(user);
            int error = hCNetSDK.NET_DVR_GetLastError();
            log.error("login error: " + error);
        }
        return true;
    }

    public Result initChannel(Integer chanNum) {
        HCNetSDK.NET_DVR_PREVIEWINFO strClientInfo = new HCNetSDK.NET_DVR_PREVIEWINFO();
        strClientInfo.read();
        strClientInfo.hPlayWnd = null;  //窗口句柄，从回调取流不显示一般设置为空
        strClientInfo.lChannel = chanNum;  //通道号
        strClientInfo.dwStreamType = 0; //0-主码流，1-子码流，2-三码流，3-虚拟码流，以此类推
        strClientInfo.dwLinkMode = 0; //连接方式：0- TCP方式，1- UDP方式，2- 多播方式，3- RTP方式，4- RTP/RTSP，5- RTP/HTTP，6- HRUDP（可靠传输） ，7- RTSP/HTTPS，8- NPQ
        strClientInfo.bBlocked = 1;  //0- 非阻塞取流，1- 阻塞取流
        strClientInfo.write();
        int lPreviewHandle = hCNetSDK.NET_DVR_RealPlay_V40(lUserID, strClientInfo, null, null);
        if (lPreviewHandle <= -1) {
            int error;
            error = hCNetSDK.NET_DVR_GetLastError();
            log.info("error code1 is ：{}", error);
        }
        previewHandle = lPreviewHandle;
        return Result.ok();
    }

    public Result closeChannel() {
        Boolean bStopSaveVideo = hCNetSDK.NET_DVR_StopSaveRealData(previewHandle);
        if (bStopSaveVideo == false) {
            int iErr = hCNetSDK.NET_DVR_GetLastError();
            System.out.println("停止录像失败" + iErr);
            return Result.error();
        }
        System.out.println("停止录像成功");
        hCNetSDK.NET_DVR_StopRealPlay(previewHandle);
        return Result.ok();
    }

    public Result changeCameraStatus(Integer command, Integer isStop, Integer speed, Integer chanNum) {
        if (Objects.isNull(previewHandle)) {
            initChannel(chanNum);
        }
        log.info("previewHandle: {}", previewHandle);
        boolean success = false;
        if (isStop.equals(0) && command > 20) {
            success = hCNetSDK.NET_DVR_PTZControlWithSpeed(previewHandle, command, isStop, speed);
        }
        if (isStop.equals(1) && command > 20) {
            success = hCNetSDK.NET_DVR_PTZControlWithSpeed(previewHandle, command, isStop, speed);
        }
        if (isStop.equals(0) && command < 20) {
            success = hCNetSDK.NET_DVR_PTZControlWithSpeed(previewHandle, command, isStop, speed);
        }
        if (isStop.equals(1) && command < 20) {
            success = hCNetSDK.NET_DVR_PTZControlWithSpeed(previewHandle, command, isStop, speed);
        }
        if (success) {
            log.info("operate success");
            return Result.ok();
        } else {
            log.error("change state error:{}", hCNetSDK.NET_DVR_GetLastError());
        }
        return Result.error();
    }

    public Result changeZoom(Integer xTop, Integer xBottom, Integer yTop, Integer yBottom, Integer chanNum) {
        if (Objects.isNull(previewHandle)) {
            initChannel(chanNum);
        }
        HCNetSDK.NET_DVR_POINT_FRAME frame = new HCNetSDK.NET_DVR_POINT_FRAME();
        frame.xTop = xTop;
        frame.xBottom = xBottom;
        frame.yTop = yTop;
        frame.yBottom = yBottom;
        boolean res = hCNetSDK.NET_DVR_PTZSelZoomIn(previewHandle, frame);
        if (res) {
            return Result.ok();
        }
        return Result.error();
    }

    public Result setPos() {
        hCNetSDK.NET_DVR_PTZPreset(previewHandle, HCNetSDK.SET_PRESET, 1);
        return Result.ok();
    }

    public Result getPosList(Integer chanNum) {
        return Result.ok(posMap.get(chanNum));
    }

    public void clearPos() {
        hCNetSDK.NET_DVR_PTZPreset(previewHandle, HCNetSDK.CLE_PRESET, 1);
    }

    public Result gotoPos() {
        hCNetSDK.NET_DVR_PTZPreset(previewHandle, HCNetSDK.GOTO_PRESET, 1);
        clearPos();
        return Result.ok();
    }

    public Result stop() {
        hCNetSDK.NET_DVR_Logout_V30(lUserID);
        return Result.ok();
    }
}